package com.etone.smartAudit.service;

import com.etone.smartAudit.domain.Admin;
import com.etone.smartAudit.domain.AuditExceptionDownload;
import com.etone.smartAudit.enums.DownloadState;
import com.etone.smartAudit.enums.DownloadType;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.mapper.AuditExceptionCityReportMapper;
import com.etone.smartAudit.mapper.AuditExceptionDownloadMapper;
import com.etone.smartAudit.utils.DateUtil;
import com.etone.smartAudit.utils.word.AuditExceptionForeignReportUtil;
import com.etone.smartAudit.utils.word.WordTemplateUtil;
import com.etone.smartAudit.vo.PagerVO;
import com.etone.smartAudit.vo.auditExceptionCityReport.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: sovy
 * @date: 2021/1/18
 * @description: 审计风险地市报告
 */

@Service
@Slf4j
public class AuditExceptionCityReportService {

    @Autowired
    private AuditExceptionCityReportMapper auditExceptionCityReportMapper;

    @Autowired
    private AuditExceptionDownloadMapper auditExceptionDownloadMapper;

    @Value("${audit.filePath}")
    private String filePath;

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Value("${spring.datasource.database}")
    private String database;

    @Value("${spring.gpds.schema}")
    private String gpSchema;

    @Value("${spring.gpds.database}")
    private String gpDatabase;

    @Value("${spring.gpds.ip}")
    private String gpIp;

    @Value("${spring.gpds.port}")
    private Integer gpPort;

    @Value("${spring.gpds.username}")
    private String gpUsername;

    @Value("${spring.gpds.password}")
    private String gpPassword;


    /**
     * 获取地市
     *
     * @return
     */
    public List<String> findAllCity() {
        return auditExceptionCityReportMapper.findAllCity();
    }

    /**
     * 获取列表
     *
     * @param cityName
     * @param date
     * @param page
     * @param pageSize
     * @return
     */
    public AuditExceptionCityReportVO list(String cityName, String date, int page, int pageSize) {

        //获取表头
        List<AuditExceptionCityReportHeaderVO> heads = auditExceptionCityReportMapper.findTableColumn(database);

        //获取数据
        Page<Map<String, Object>> dataPage = PageHelper.startPage(page, pageSize).doSelectPage(() -> auditExceptionCityReportMapper.findList(cityName, date, null, null));

        return AuditExceptionCityReportVO.builder().head(heads).data(PagerVO.of(dataPage)).build();
    }

    /**
     * 导出单个报告
     *
     * @param id
     * @param fields
     * @param response
     */
    public void exportReport(Integer id, List<String> fields, HttpServletResponse response) {
        Map<String, Object> dataMap = auditExceptionCityReportMapper.findOne(id, fields);
        String today = DateUtil.turnJavaUtilDateToStrDate(new Date(), "yyyy年MM月dd日");
        dataMap.put("date", dataMap.get("date").toString().replaceAll("-", "年") + "月");
        dataMap.put("today", today);
        List<AuditExceptionCityReportSpecialVO> specialVOS = parseSpecialMap(dataMap);
        dataMap.put("specials", specialVOS);
        dataMap.put("typeCnt", specialVOS.stream().mapToInt(special -> special.getTypes().size()).sum());
        WordTemplateUtil.export(dataMap, response);
    }


    /**
     * 批量导出报告
     *
     * @param fields
     * @param reportPeriods
     * @param areas
     */
    public void batchExportReport(List<String> fields, String reportPeriods, List<String> areas, Admin admin) {
        //获取数据
        List<Map<String, Object>> datas = new ArrayList<>();
        String today = DateUtil.turnJavaUtilDateToStrDate(new Date(), "yyyy年MM月dd日");
        //初始化下载中心
        String fileName = "批量地市报告" + DateUtil.parseDate2String(new Date(), "yyyyMMddHHmmss") + ".zip";
        AuditExceptionDownload auditExceptionDownload = AuditExceptionDownload.builder().originName(DownloadType.EXCEPTION_REPORT.getRemark())
                .remark(reportPeriods + fileName).createId(admin.getId()).createTime(new Date())
                .type(DownloadType.EXCEPTION_REPORT.getValue()).state(DownloadState.UNDER_WAY.getValue()).build();
        auditExceptionDownloadMapper.insert(auditExceptionDownload);
        for (String area : areas) {
            Map<String, Object> data = new HashMap<>();
            if (!CollectionUtils.isEmpty(fields)) {
                for (String field : fields) {
                    data.put(field, null);
                }
            }

            data.put("city", area);
            data.put("reportPeriods", reportPeriods);
            data.put("today", today);
            List<AuditExceptionCityReportSpecialVO> specialVOS = parseSpecialMap(data);
            data.put("specials", specialVOS);
            data.put("typeCnt", specialVOS.stream().mapToInt(special -> special.getTypes().size()).sum());
            datas.add(data);
        }
        long startTime = System.currentTimeMillis();
        MultipartFile multipartFile;
        try {
            multipartFile = WordTemplateUtil.batchExport(datas, fileName);
        } catch (Exception e) {
            auditExceptionDownload.setState(DownloadState.FINISH.getValue());
            auditExceptionDownloadMapper.updateById(auditExceptionDownload);
            throw new CustomParameterizedException("文件导出失败");
        }
        long endTime = System.currentTimeMillis();
        String path = "/audit/report";


        uploadFile(fileName, path, auditExceptionDownload, multipartFile);
        auditExceptionDownload.setUseTime(endTime - startTime);
        auditExceptionDownloadMapper.updateById(auditExceptionDownload);

    }

    public List<AuditExceptionCityReportSpecialVO> parseSpecialMap(Map<String, Object> sources) {
        List<AuditExceptionCityReportSpecialVO> specials = new ArrayList<>();
        for (String key : sources.keySet()) {
            if (key.contains("z_") && sources.get(key) != null) {
                String[] keys = key.split("_");
                if (keys.length == 2) {
                    parseSpecial(sources, specials, key, sources.get(key).toString());


                } else if (keys.length == 3) {
                    parseType(sources, specials, key, sources.get(key).toString());

                } else if (keys.length == 4) {
                    AuditExceptionCityReportTypeVO typeVO = parseType(sources, specials, key, sources.get(key).toString());

                    List<AuditExceptionCityReportParamVO> params = typeVO.getParams();
                    params.add(AuditExceptionCityReportParamVO.builder().key(key).value(sources.get(key).toString()).build());
                }
            }
        }

        //去掉参数空值
        for (AuditExceptionCityReportSpecialVO special : specials) {
            for (AuditExceptionCityReportTypeVO type : special.getTypes()) {
                List<AuditExceptionCityReportParamVO> params = type.getParams().stream().filter(param -> !StringUtils.isEmpty(param.getValue())).collect(Collectors.toList());
                type.setParams(params);
            }
        }
        //去掉审计点空值
        for (AuditExceptionCityReportSpecialVO special : specials) {
            List<AuditExceptionCityReportTypeVO> types = special.getTypes().stream().filter(type -> !StringUtils.isEmpty(type.getName()) && !CollectionUtils.isEmpty(type.getParams())).collect(Collectors.toList());
            special.setTypes(types);
        }
        //去掉专题空值
        specials = specials.stream().filter(special -> !StringUtils.isEmpty(special.getName()) && !CollectionUtils.isEmpty(special.getTypes())).collect(Collectors.toList());

        //排序
        specials.sort(Comparator.comparing(a -> Integer.valueOf(a.getKey().replaceAll("z", "1").replaceAll("_", "0"))));
        for (AuditExceptionCityReportSpecialVO special : specials) {

            special.getTypes().sort(Comparator.comparing(a -> Integer.valueOf(a.getKey().replaceAll("z", "1").replaceAll("_", "0"))));
            for (AuditExceptionCityReportTypeVO type : special.getTypes()) {
                type.getParams().sort(Comparator.comparing(a -> Integer.valueOf(a.getKey().replaceAll("z", "1").replaceAll("_", "0"))));
            }
        }
        return specials;
    }

    /**
     * 获取专题
     *
     * @param sources
     * @param specials
     * @param key
     * @param name
     * @return
     */
    public AuditExceptionCityReportSpecialVO parseSpecial(Map<String, Object> sources, List<AuditExceptionCityReportSpecialVO> specials, String key, String name) {
        AuditExceptionCityReportSpecialVO specialVO;
        Optional<AuditExceptionCityReportSpecialVO> first = specials.stream().filter(special -> special.getKey().equals(key)).findFirst();
        if (first.isPresent()) {
            specialVO = first.get();
            specialVO.setName(sources.get(key) == null ? "" : sources.get(key).toString());

        } else {
            specialVO = AuditExceptionCityReportSpecialVO.builder().key(key).name(name).types(new ArrayList<>()).build();
            specials.add(specialVO);
        }
        return specialVO;
    }

    /**
     * 获取审计点
     *
     * @param sources
     * @param specials
     * @param key
     * @param name
     * @return
     */
    public AuditExceptionCityReportTypeVO parseType(Map<String, Object> sources, List<AuditExceptionCityReportSpecialVO> specials, String key, String name) {

        AuditExceptionCityReportTypeVO typeVO;

        String[] keys = key.split("_");
        AuditExceptionCityReportSpecialVO specialVO = parseSpecial(sources, specials, keys[0] + "_" + keys[1], "");


        String typeKey = keys[0] + "_" + keys[1] + "_" + keys[2];
        List<AuditExceptionCityReportTypeVO> types = specialVO.getTypes();
        Optional<AuditExceptionCityReportTypeVO> typeFirst = types.stream().filter(type -> type.getKey().equals(typeKey)).findFirst();
        if (typeFirst.isPresent()) {
            typeVO = typeFirst.get();
            typeVO.setName(sources.get(typeKey) == null ? "" : sources.get(typeKey).toString());
        } else {
            typeVO = AuditExceptionCityReportTypeVO.builder().key(typeKey).name(name).params(new ArrayList<>()).build();
            types.add(typeVO);
        }
        return typeVO;
    }

    /**
     * 批量导出地市部分风险扫描报告
     *
     * @param startTime
     * @param endTime
     * @param citys
     * @param admin
     */
    public void cityExceptionReportBatchExport(String startTime, String endTime, List<String> citys, Admin admin) {
        //创建下载中心数据
        String fileName = "地市风险扫描报告" + DateUtil.parseDate2String(new Date(), "yyyyMMddHHmmss") + ".zip";
        String start = startTime.substring(0, 4) + "年" + startTime.substring(5, 7) + "月-";
        String end = endTime.substring(0, 4) + "年" + endTime.substring(5, 7) + "月";
        AuditExceptionDownload exceptionDownload = AuditExceptionDownload.builder().
                createId(admin.getId()).createTime(new Date()).originName(DownloadType.CITY_EXCEPTION_REPORT.getRemark()).type(DownloadType.CITY_EXCEPTION_REPORT.getValue())
                .remark(start + end + fileName).state(DownloadState.UNDER_WAY.getValue()).build();
        auditExceptionDownloadMapper.insert(exceptionDownload);
        long startMill = System.currentTimeMillis();
        MultipartFile file;
        try {
            file = AuditExceptionForeignReportUtil.batchExport(gpIp, gpUsername, gpPassword, gpPort, gpDatabase, gpSchema, startTime, endTime, citys, fileName);
        } catch (Exception e) {
            exceptionDownload.setState(DownloadState.FINISH.getValue());
            auditExceptionDownloadMapper.updateById(exceptionDownload);
            throw new CustomParameterizedException("文件导出失败");
        }
        long endMill = System.currentTimeMillis();
        String path = "/audit/report/city";
        uploadFile(fileName, path, exceptionDownload, file);
        exceptionDownload.setUseTime(endMill - startMill);
        auditExceptionDownloadMapper.updateById(exceptionDownload);
    }

    /**
     * 文件上传到下载中心
     *
     * @param fileName
     * @param path
     * @param exceptionDownload
     * @param file
     * @return
     */
    public AuditExceptionDownload uploadFile(String fileName, String path, AuditExceptionDownload exceptionDownload, MultipartFile file) {
        try {
            //原文件名
            String originalFilename = file.getOriginalFilename();
            //扩展名
            String extension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
            //文件名
            String uploadName = System.currentTimeMillis() + "_" + String.format("%05d", new Random().nextInt(100000)) + "." + extension;
            String relativePath = path + "/" + uploadName;
            String url = contextPath + "/upload/files" + relativePath;

            File curPath = new File(filePath + path);
            if (!curPath.exists()) {
                curPath.mkdirs();
            }

            File curFile = new File(filePath + relativePath);

            exceptionDownload.setExportTime(new Date());
            exceptionDownload.setExtension(extension);
            exceptionDownload.setName(fileName);
            exceptionDownload.setUrl(url);
            exceptionDownload.setSize(file.getSize());
            exceptionDownload.setPath(relativePath);
            exceptionDownload.setName(uploadName);
            exceptionDownload.setState(DownloadState.FINISH.getValue());
            file.transferTo(curFile);
        } catch (Exception e) {
            exceptionDownload.setState(DownloadState.FAIL.getValue());
            auditExceptionDownloadMapper.updateById(exceptionDownload);
            log.info("导出失败:{}", e.getMessage());
            e.printStackTrace();
        }
        return exceptionDownload;
    }
}
